import pandas as pd
import numpy as np
from itertools import combinations
from collections import defaultdict
import matplotlib.pyplot as plt
import matplotlib
from mlxtend.frequent_patterns import apriori, association_rules

# 设置中文字体支持
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False

def load_and_preprocess_data():
    """
    加载并预处理数据
    """
    # 读取数据
    base = pd.read_csv('customer_base.csv')
    behavior = pd.read_csv('customer_behavior_assets.csv')
    
    # 获取最新的客户行为数据
    latest_behavior = behavior.sort_values('stat_month').groupby('customer_id').tail(1)
    
    # 合并数据
    merged = pd.merge(base, latest_behavior, on='customer_id', how='inner')
    
    return merged

def prepare_transaction_data(data):
    """
    准备交易数据用于关联分析
    """
    # 创建产品标志列的副本用于分析
    transaction_data = data[['customer_id', 'deposit_flag', 'financial_flag', 
                            'fund_flag', 'insurance_flag']].copy()
    
    # 将数字标志(1/0)转换为布尔值(True/False)
    flag_columns = ['deposit_flag', 'financial_flag', 'fund_flag', 'insurance_flag']
    for col in flag_columns:
        transaction_data[col] = transaction_data[col].astype(bool)
    
    # 处理缺失值
    transaction_data.fillna(False, inplace=True)
    
    return transaction_data

def perform_apriori_analysis(transaction_data, min_support=0.1):
    """
    执行Apriori关联分析
    """
    # 转换数据格式
    # 只保留产品标志列
    product_data = transaction_data[['deposit_flag', 'financial_flag', 
                                   'fund_flag', 'insurance_flag']].astype(bool)
    
    # 重命名列以提高可读性
    product_data.columns = ['存款', '理财', '基金', '保险']
    
    # 使用Apriori算法找出频繁项集
    frequent_itemsets = apriori(product_data, min_support=min_support, use_colnames=True)
    
    # 生成关联规则
    if len(frequent_itemsets) > 1:
        rules = association_rules(frequent_itemsets, metric="confidence", min_threshold=0.5)
        return frequent_itemsets, rules
    else:
        print("未找到满足最小支持度的频繁项集")
        return frequent_itemsets, None

def analyze_product_combinations(transaction_data):
    """
    分析产品组合模式
    """
    # 统计各产品持有情况
    product_stats = {
        '存款': transaction_data['deposit_flag'].sum(),
        '理财': transaction_data['financial_flag'].sum(),
        '基金': transaction_data['fund_flag'].sum(),
        '保险': transaction_data['insurance_flag'].sum()
    }
    
    # 统计产品组合
    combination_counts = defaultdict(int)
    
    # 遍历每个客户，统计其持有的产品组合
    for _, row in transaction_data.iterrows():
        products = []
        if row['deposit_flag']:
            products.append('存款')
        if row['financial_flag']:
            products.append('理财')
        if row['fund_flag']:
            products.append('基金')
        if row['insurance_flag']:
            products.append('保险')
        
        # 将产品组合转换为元组并排序，确保一致性
        product_tuple = tuple(sorted(products))
        combination_counts[product_tuple] += 1
    
    return product_stats, combination_counts

def visualize_association_rules(frequent_itemsets, rules):
    """
    可视化关联规则分析结果
    """
    if frequent_itemsets.empty:
        print("没有频繁项集用于可视化")
        return
    
    # 可视化频繁项集支持度
    plt.figure(figsize=(15, 5))
    
    # 子图1：频繁项集支持度
    plt.subplot(1, 3, 1)
    frequent_itemsets_sorted = frequent_itemsets.sort_values('support', ascending=False)
    itemsets_labels = [', '.join(list(itemset)) for itemset in frequent_itemsets_sorted['itemsets']]
    support_values = frequent_itemsets_sorted['support'].values
    
    bars = plt.bar(range(len(itemsets_labels)), support_values, 
                   color=plt.cm.viridis(np.linspace(0, 1, len(itemsets_labels))))
    plt.xticks(range(len(itemsets_labels)), itemsets_labels, rotation=45, ha='right')
    plt.ylabel('支持度')
    plt.title('频繁项集支持度')
    plt.grid(axis='y', alpha=0.3)
    
    # 在柱状图上添加数值标签
    for i, (bar, value) in enumerate(zip(bars, support_values)):
        plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01, 
                f'{value:.3f}', ha='center', va='bottom', fontsize=8)
    
    # 子图2：关联规则置信度
    if rules is not None and not rules.empty:
        plt.subplot(1, 3, 2)
        rules_sorted = rules.sort_values('confidence', ascending=False)
        rules_labels = [f"{', '.join(list(ant))} → {', '.join(list(con))}" 
                       for ant, con in zip(rules_sorted['antecedents'], rules_sorted['consequents'])]
        confidence_values = rules_sorted['confidence'].values
        
        bars = plt.bar(range(len(rules_labels)), confidence_values, 
                       color=plt.cm.plasma(np.linspace(0, 1, len(rules_labels))))
        plt.xticks(range(len(rules_labels)), rules_labels, rotation=45, ha='right')
        plt.ylabel('置信度')
        plt.title('关联规则置信度')
        plt.grid(axis='y', alpha=0.3)
        
        # 在柱状图上添加数值标签
        for i, (bar, value) in enumerate(zip(bars, confidence_values)):
            plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01, 
                    f'{value:.3f}', ha='center', va='bottom', fontsize=8)
        
        # 子图3：关联规则提升度
        plt.subplot(1, 3, 3)
        lift_values = rules_sorted['lift'].values
        
        bars = plt.bar(range(len(rules_labels)), lift_values, 
                       color=plt.cm.coolwarm(np.linspace(0, 1, len(rules_labels))))
        plt.xticks(range(len(rules_labels)), rules_labels, rotation=45, ha='right')
        plt.ylabel('提升度')
        plt.title('关联规则提升度')
        plt.grid(axis='y', alpha=0.3)
        
        # 在柱状图上添加数值标签
        for i, (bar, value) in enumerate(zip(bars, lift_values)):
            plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01, 
                    f'{value:.3f}', ha='center', va='bottom', fontsize=8)
    else:
        # 如果没有关联规则，显示一个空的子图
        plt.subplot(1, 3, 2)
        plt.text(0.5, 0.5, '无关联规则', ha='center', va='center', transform=plt.gca().transAxes)
        plt.title('关联规则置信度')
        plt.axis('off')
        
        plt.subplot(1, 3, 3)
        plt.text(0.5, 0.5, '无关联规则', ha='center', va='center', transform=plt.gca().transAxes)
        plt.title('关联规则提升度')
        plt.axis('off')
    
    plt.tight_layout()
    plt.savefig('image_show/association_rules_analysis.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    print("关联规则分析图表已保存到 association_rules_analysis.png")

def visualize_product_analysis(product_stats, combination_counts):
    """
    可视化产品分析结果
    """
    # 绘制各产品持有情况
    plt.figure(figsize=(15, 5))
    
    # 子图1：各产品持有数量
    plt.subplot(1, 3, 1)
    products = list(product_stats.keys())
    counts = list(product_stats.values())
    bars = plt.bar(products, counts, color=['skyblue', 'lightgreen', 'lightcoral', 'lightyellow'])
    plt.title('各产品持有客户数量')
    plt.ylabel('客户数量')
    
    # 在柱状图上添加数值标签
    for bar, count in zip(bars, counts):
        plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 50, 
                str(count), ha='center', va='bottom')
    
    # 子图2：最常见的产品组合（前10个）
    plt.subplot(1, 3, 2)
    # 按数量排序并取前10个组合
    sorted_combinations = sorted(combination_counts.items(), key=lambda x: x[1], reverse=True)[:10]
    combinations_labels = ['+'.join(combo) if combo else '无产品' for combo, _ in sorted_combinations]
    combinations_counts = [count for _, count in sorted_combinations]
    
    bars = plt.barh(range(len(combinations_labels)), combinations_counts, 
                   color=plt.cm.viridis(np.linspace(0, 1, len(combinations_labels))))
    plt.yticks(range(len(combinations_labels)), combinations_labels)
    plt.xlabel('客户数量')
    plt.title('最常见的产品组合（前10个）')
    
    # 在条形图上添加数值标签
    for i, (bar, count) in enumerate(zip(bars, combinations_counts)):
        plt.text(bar.get_width() + 10, bar.get_y() + bar.get_height()/2, 
                str(count), ha='left', va='center')
    
    # 子图3：产品组合占比饼图（前6个）
    plt.subplot(1, 3, 3)
    top_combinations = sorted_combinations[:6]
    other_count = sum([count for _, count in sorted_combinations[6:]])
    if other_count > 0:
        top_combinations.append((('其他组合',), other_count))
    
    combinations_for_pie = [count for _, count in top_combinations]
    labels_for_pie = [('+'.join(combo) if combo else '无产品') for combo, _ in top_combinations]
    
    plt.pie(combinations_for_pie, labels=labels_for_pie, autopct='%1.1f%%', startangle=90)
    plt.title('产品组合分布（前6个+其他）')
    
    plt.tight_layout()
    plt.savefig('image_show/product_combination_analysis.png', dpi=300, bbox_inches='tight')
    plt.close()
    
    print("产品组合分析图表已保存到 product_combination_analysis.png")

def display_association_rules(frequent_itemsets, rules):
    """
    显示关联规则分析结果
    """
    print("\n=== 频繁项集 ===")
    if not frequent_itemsets.empty:
        # 按支持度排序
        frequent_itemsets = frequent_itemsets.sort_values('support', ascending=False)
        for idx, row in frequent_itemsets.iterrows():
            itemset = ', '.join(list(row['itemsets']))
            support = row['support']
            print(f"组合: {itemset} | 支持度: {support:.4f}")
    else:
        print("未找到频繁项集")
    
    print("\n=== 关联规则 ===")
    if rules is not None and not rules.empty:
        # 选择重要的列并按置信度排序
        rules_display = rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']].copy()
        rules_display = rules_display.sort_values('confidence', ascending=False)
        
        for idx, row in rules_display.iterrows():
            antecedent = ', '.join(list(row['antecedents']))
            consequent = ', '.join(list(row['consequents']))
            support = row['support']
            confidence = row['confidence']
            lift = row['lift']
            
            print(f"{antecedent} => {consequent}")
            print(f"  支持度: {support:.4f}, 置信度: {confidence:.4f}, 提升度: {lift:.4f}")
    else:
        print("未找到满足条件的关联规则")

def generate_product_recommendations(transaction_data):
    """
    基于关联规则生成产品推荐
    """
    print("\n=== 产品推荐策略 ===")
    
    # 统计只持有一种产品的客户
    single_product_customers = transaction_data[
        (transaction_data[['deposit_flag', 'financial_flag', 'fund_flag', 'insurance_flag']].sum(axis=1) == 1)
    ].copy()
    
    print(f"只持有一种产品的客户数量: {len(single_product_customers)}")
    
    # 定义产品和对应的列名
    product_mapping = {
        '存款': 'deposit_flag',
        '理财': 'financial_flag',
        '基金': 'fund_flag',
        '保险': 'insurance_flag'
    }
    
    # 分析每种单一产品的客户可能感兴趣的产品
    for product, product_flag in product_mapping.items():
        # 筛选只持有当前产品的客户
        product_customers = single_product_customers[
            single_product_customers[product_flag] == True
        ]
        
        if len(product_customers) > 0:
            print(f"\n持有'{product}'的客户可能感兴趣的产品:")
            # 检查其他产品
            other_products = {k: v for k, v in product_mapping.items() if k != product}
            for other_product, other_product_flag in other_products.items():
                # 计算转换率
                converted_customers = len(transaction_data[
                    (transaction_data[product_flag] == True) &
                    (transaction_data[other_product_flag] == True)
                ])
                
                total_product_customers = len(transaction_data[
                    transaction_data[product_flag] == True
                ])
                
                if total_product_customers > 0:
                    conversion_rate = converted_customers / total_product_customers
                    print(f"  {other_product}: 转换率 {conversion_rate:.4f} ({converted_customers}/{total_product_customers})")
        else:
            print(f"\n没有只持有'{product}'的客户")

def main():
    """
    主函数
    """
    print("开始产品组合关联分析...")
    
    # 加载数据
    print("1. 加载数据...")
    data = load_and_preprocess_data()
    print(f"数据加载完成，共 {len(data)} 条客户记录")
    
    # 准备交易数据
    print("2. 准备交易数据...")
    transaction_data = prepare_transaction_data(data)
    print("交易数据准备完成")
    
    # 分析产品组合
    print("3. 分析产品组合...")
    product_stats, combination_counts = analyze_product_combinations(transaction_data)
    
    # 显示产品统计信息
    print("\n=== 产品持有统计 ===")
    for product, count in product_stats.items():
        percentage = count / len(transaction_data) * 100
        print(f"{product}: {count} 人 ({percentage:.2f}%)")
    
    # 显示最常见的产品组合
    print("\n=== 最常见的产品组合 ===")
    sorted_combinations = sorted(combination_counts.items(), key=lambda x: x[1], reverse=True)
    for combination, count in sorted_combinations[:10]:
        if combination:
            combination_str = '+'.join(combination)
        else:
            combination_str = "无产品"
        percentage = count / len(transaction_data) * 100
        print(f"{combination_str}: {count} 人 ({percentage:.2f}%)")
    
    # 可视化分析结果
    print("4. 可视化分析结果...")
    visualize_product_analysis(product_stats, combination_counts)
    
    # 执行Apriori关联分析
    print("5. 执行Apriori关联分析...")
    frequent_itemsets, rules = perform_apriori_analysis(transaction_data, min_support=0.1)
    
    # 显示关联规则
    display_association_rules(frequent_itemsets, rules)
    
    # 可视化关联规则
    print("6. 可视化关联规则...")
    visualize_association_rules(frequent_itemsets, rules)
    
    # 生成产品推荐
    generate_product_recommendations(transaction_data)
    
    # 保存分析结果
    combination_df = pd.DataFrame([
        {'combination': '+'.join(combo) if combo else '无产品', 'count': count, 
         'percentage': count / len(transaction_data) * 100}
        for combo, count in combination_counts.items()
    ])
    combination_df.to_csv('image_show/product_combination_results.csv', index=False)
    print("\n产品组合分析结果已保存到 product_combination_results.csv")
    
    print("\n产品组合关联分析完成！")

if __name__ == '__main__':
    main()